#include "buildlog.h"

#include <QDir>
#include <QRegExp>

BuildLog::BuildLog( QWidget * parent )
        : QTextEdit(parent)
{
    setReadOnly(true);
}

void BuildLog::mouseDoubleClickEvent( QMouseEvent * /*event*/ )
{
    // First highlight the line double-clicked
    QTextCursor cursor = textCursor();
    cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
    cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
    setTextCursor( cursor );
    //
    BlockLogBuild *blockUserData = (BlockLogBuild*)cursor.block().userData();
    // If blockUserData is null, the line doesn't contains "error" or "warning", we quit this function.
    if ( !blockUserData )
        return;
    QString text = cursor.block().text();

    if (blockUserData->sourceType() == TYPE_C_CPP) {
        parseGccOutput(blockUserData->directory(), text, true);
    }
    if (blockUserData->sourceType() == TYPE_PASCAL) {
         parseFpcOutput(blockUserData->directory(), text, true);
    }
}

void BuildLog::slotMessagesBuild (QString list, QString directory, SourceType type)
{
    foreach (QString message, list.split("\n"))
    {
        if ( !message.isEmpty() ) {
            message.remove( "\r" );
            setTextColor( Qt::black );

            MessageType mtype = MT_NORMAL;
            if (type == TYPE_C_CPP) {
                mtype = parseGccOutput(directory, message, false);
            } else if (type == TYPE_PASCAL) {
                mtype = parseFpcOutput(directory, message, false);
            }

            if ( mtype == MT_ERROR) {
                setTextColor( Qt::red );
                emit incErrors();
            } else if ( mtype == MT_WARNING) {
                setTextColor( Qt::blue );
                emit incWarnings();
            }
            append( message );
            if ( !directory.isEmpty() ) {
                QTextCursor cursor = textCursor();
                BlockLogBuild *blockUserData = new BlockLogBuild(directory, type);
                cursor.block().setUserData( blockUserData );
                setTextCursor( cursor );
            }
        }
    }
    // Move the cursor to the bottom. Ensure it is visible.
    QTextCursor cursor = textCursor();
    cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
    setTextCursor( cursor );
}


/*  If your language is not translated in QDevelop and if g++ display the errors and warnings in your language, 
modify the two strings below "error" and "warning" to adapt in your language. Also have a look at editor.cpp*/
bool BuildLog::containsError(QString /*dirname*/, QString message, SourceType /*type*/)
{
    return ( (message.toLower().contains("error") || message.toLower().contains( tr("error").toLower() ))
                && !message.contains("------") && !message.startsWith("make:"));
}

bool BuildLog::containsWarning(QString /*dirname*/, QString message, SourceType /*type*/)
{
    return ( (message.toLower().contains( "warning") || message.toLower().contains( tr("warning").toLower() ))
              && !message.startsWith("make:"));
}

/* Gcc output samples.
 * xxxx.cpp:5: warning: unused variable 'a'
 * xxxx.cpp:31: error: `rs' was not declared in this scope
 * xxxx.cpp:17:23: aaaa.h: No such file or directory
 *
 * xxxx.cpp:4: note: candidates are: int foo1(int) <near match>
 * xxxx.cpp:10: note:                int foo1(char) <near match>
 */
BuildLog::MessageType BuildLog::parseGccOutput(QString dirname, QString message, bool sig)
{
    QString directory = dirname;
    int fieldcnt = message.count(": ");
    if (fieldcnt < 2) {
        /* This is not an error or warning message. */
        return MT_NORMAL;
    }
    QString fileLine = message.section(": ", 0, 0);
    QString reasonOrFile = message.section(": ", 1, 1);
    QString detail = message.section(": ", 2, 2);

    MessageType type = MT_NORMAL;
    if (!directory.isEmpty()) {
        if (reasonOrFile.toLower() == "warning" || reasonOrFile.toLower() == tr("warning")) {
            type = MT_WARNING;
        } else if (reasonOrFile.toLower() == "note" || reasonOrFile.toLower() == tr("note")) {
            type = MT_WARNING;
        } else if (reasonOrFile.toLower() == "error" || reasonOrFile.toLower() == tr("error")) {
            type = MT_ERROR;
        } else {
            type = MT_WARNING;
        }
    }
    if (sig && type != MT_NORMAL) {
        QString fname = fileLine.section(":", 0, 0).replace("\\", "/").replace("//", "/");
        bool ok;
        int numLine = fileLine.section(":", 1, 1).toInt(&ok,10);
        //if (!ok) {
        //    int numLine = fileLine.mid(fileLine.indexOf);
        //}

        QString absoluteName = QDir(directory+'/'+fname).absolutePath();
        emit buildLogClicked(absoluteName, numLine);
    }
    return type;
}

/* Fpc output sample
aaaa.pas(3,6) Fatal: Can't find unit xxxx used by testpas
aaaa.pas(12,2) Error: Identifier not found "j"
aaaa.pas(27,23) Warning: Mixing signed expressions and longwords gives a 64bit result
aaaa.pas(117,5) Note: Local variable "time1" not used
*/
BuildLog::MessageType BuildLog::parseFpcOutput(QString dirname, QString message, bool sig)
{
    QRegExp rx("\\([\\d]+,[\\d]+\\) (Fatal|Error|Warning|Note):");

    if (dirname.isEmpty())
        return MT_NORMAL;
    if (rx.indexIn(message) == -1) {
        return MT_NORMAL;
    }
    /* e.g. message == aaaa.pas(12,2) Error: Identifier not found "j" */
    QString fileLine = message.section(":", 0, 0);
    /* fileLine == aaaa.pas(12,2) Error */
    QString reason = fileLine.section(" ", 1, 1);
    /* reason == Error */
    QString fileLnum = fileLine.section(" ", 0, 0);
    /* fileNum == aaaa.pas(12,2) */
    fileLnum = fileLnum.replace("(", " ");
    fileLnum = fileLnum.replace(")", " ");
    fileLnum = fileLnum.replace(",", " ");
    /* fileLnum == aaaa.pas 12,2 */
    QString fname = fileLnum.section(" ", 0, 0);
    /* fname == aaaa.pas */
    int line = fileLnum.section(" ", 1, 1).toInt();
    //int index = fileLnum.section(" ", 2, 2).toInt();

    MessageType type = MT_NORMAL;

    if (reason == "Fatal" || reason == "Error")
        type = MT_ERROR;
    if (reason == "Warning" || reason == "Note")
        type = MT_WARNING;

    if (sig && type != MT_NORMAL) {
        fname = fname.replace("\\", "/").replace("//", "/");

        QString absoluteName = QDir(dirname+'/'+fname).absolutePath();
        emit buildLogClicked(absoluteName, line);
    }
    return type;
}


